'''
Description: 
Author: Tyu
Date: 2021-12-12 22:44:16
LastEditTime: 2022-02-27 11:17:48
LastEditors: Tyu
'''
# encoding=utf8
import time
import traceback
from math import ceil

import requests
import re
import json
import os
import shutil
from contextlib import closing
from tqdm import tqdm
from Downloader import Downloader

from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

debug = True
debug = False


class bilibili(object):
    def __init__(self, url, ffmpegPath):
        '''
        @param {url}: 视频链接
        '''
        self.url = url
        self.bid = re.findall(r'video/([^?/]+)', url)[0]
        self.ffmpegPath = ffmpegPath
        self.videoname = ''
        self.audioname = ''
        self.headers = {
            'authority': 'www.bilibili.com',
            'cache-control': 'max-age=0',
            'upgrade-insecure-requests': '1',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
            'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
            'sec-fetch-site': 'none',
            'sec-fetch-mode': 'navigate',
            'sec-fetch-user': '?1',
            'sec-fetch-dest': 'document',
            'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
            '$cookie': 'buvid3=7AE1B2D4-F2FF-AD0B-3692-A8CAC294DD1E36432infoc; _uuid=57109855D-5C9B-BB37-D1E5-FD8AB1C26F6837780infoc; buvid4=6FA78DB6-2FDF-1CBD-8F75-FE52DDB9254237350-022040818-vlkk8cym8W0BdF0LsM/Wyw%3D%3D; sid=6um0fsku; fingerprint=3e9bb0ea20dca9071ae693098a4add2a; buvid_fp_plain=undefined; buvid_fp=03f3e95f8521a85db8e9af4aa6343dbf; DedeUserID=508095722; DedeUserID__ckMd5=737d3b4dc4d484ad; SESSDATA=2ba028ec%2C1664965853%2C375e5*41; bili_jct=20468021692699e02666bbfc62c3df58; CURRENT_BLACKGAP=0; blackside_state=0; rpdid=|(ku||kJmuk|0J\'uYRmRuJmlm; i-wanna-go-back=-1; b_ut=5; nostalgia_conf=-1; PVID=1; CURRENT_QUALITY=116; bp_video_offset_508095722=649584842810327000; b_lsid=812F6772_18032596F35; innersign=1; CURRENT_FNVAL=4048',
            'referer': 'https://www.bilibili.com/',
        }
        if not os.path.exists('video'):
            os.mkdir('video')

    def getaid(self, bid):
        url = rf'https://www.bilibili.com/video/{bid}'
        a = requests.get(url, headers=self.headers)
        if debug:
            with open('bilibili.html', 'w', encoding='utf-8') as f:
                f.write(a.text)
        if bid[:2] == 'av':
            aid = bid[2:]
        else:
            # aid = re.findall(r'"' + bid + r'":\{"aid":(.*?),', a.text)[0]
            p = rf'"aid":(\d+?),"bvid":"{bid}"'
            print('p:', p)
            aid = re.findall(p, a.text)[0]
        title = re.findall(
            r'<h1 [^<>]*?title="(.*?)" class="video-title', a.text)[0]
        return aid, title

    def send_requests(self):
        respones = requests.get(
            url=self.url, headers=self.headers).content.decode()
        if debug:
            with open('bilibili.html', 'w', encoding='utf-8') as f:
                f.write(respones)
        respones_re = re.findall(
            "<script>window.__playinfo__=(.*?)</script>", respones)[0]
        if debug:
            with open('bilibili.json', 'w', encoding='utf-8') as f:
                f.write(respones_re)
        respones_json = json.loads(respones_re)
        B_audio = respones_json["data"]["dash"]["audio"][0]["baseUrl"]
        B_video = respones_json["data"]["dash"]["video"][0]["baseUrl"]
        return B_video, B_audio

    def validateTitle(self, title):
        '''
        @description: 格式化文件名
        @return {str}: 合法的文件名
        '''
        rstr = r"[/\\\:\*\?\"\<\>\|]"  # '\ : * ? " < > |'
        new_title = re.sub(rstr, "_", title)  # 替换为下划线
        return new_title

    def getMp4(self, title):
        '''
        @description: 获取视频文件
        @param {str} title: 视频文件命名
        '''
        title = self.validateTitle(title)
        print('title', title)
        # if os.path.exists('./video/' + title + '.mp4'):
        #     print(f'{title}.mp4 文件已存在，即将跳过下载')
        #     return

        url_video, url_audio = self.send_requests()
        downloader_video_name = f"cache/{self.videoname}.mp4".replace(' ', '_')
        downloader_audio_name = f"cache/{self.audioname}.mp3".replace(' ', '_')

        print("开始下载视频文件...")
        if not self.download(url_video, downloader_video_name):
            print('视频文件下载出错！')
            return False

        print("开始下载音频文件...")
        if not self.download(url_audio, downloader_audio_name):
            print('音频文件下载出错！')
            return False

        print("开始进行音频视频合并...")

        filename = 'video/' + title + '.mp4'
        ffmpeg_filename = filename.replace(' ', '_')
        try:
            os.remove(filename)
        except:
            pass
        ex_cmd = self.ffmpegPath + rf" -i {downloader_video_name} -i {downloader_audio_name} -c:v copy -c:a aac -strict experimental {ffmpeg_filename}"
        if debug:
            print(ex_cmd)
        os.system(ex_cmd)
        if filename != ffmpeg_filename:
            os.rename(ffmpeg_filename, filename)
        if os.path.exists(filename):
            os.remove(downloader_video_name)
            os.remove(downloader_audio_name)
        file = os.path.realpath(filename)
        if debug:
            print('视频的绝对路径：', file)
        os.system(f'explorer /select, {file}')
        return True

    def download(self, url, filename):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
            'Accept': '*/*',
            'Accept-Language': 'en-US,en;q=0.5',
            'Accept-Encoding': 'gzip, deflate, br',
            'Range': 'bytes=0-',
            'Referer': 'https://www.bilibili.com/video/' + self.bid,
            'Origin': 'https://www.bilibili.com',
            'Connection': 'keep-alive',
        }
        downloader = Downloader(url, filename, headers)
        if not downloader.main():
            return False
        return True

    def init(self):
        if not os.path.exists('./video'):
            os.mkdir('./video')
        if not os.path.exists('./cache'):
            os.mkdir('./cache')

    def run(self):
        if debug:
            print('bid', self.bid)
        aid, title = self.getaid(self.bid)
        self.videoname = self.validateTitle(title[:30])
        self.audioname = self.validateTitle(title[:30])
        if debug:
            print('aid', aid)
        if not self.getMp4(title):
            return False
        print('完毕！')
        return True

    def main(self):
        self.init()
        for bugTimes in range(2):
            try:
                if self.run():
                    return True
            except Exception as e:
                print(traceback.format_exc())
                print(f'运行出错，正在重试！bugTimes:{bugTimes}')
        return False


def top():
    with open('下载队列.txt', 'r') as f:
        a = f.readlines()
    if len(a) == 0:
        return None
    return a[0].strip('\n')


def pop():
    with open('下载队列.txt', 'r') as f:
        a = f.readlines()
    with open('下载队列.txt', 'w') as f:
        f.writelines(a[1:])


def logError(url):
    with open('错误队列.txt', 'a') as f:
        f.write(url + '\n')


def main():
    print('开启服务...')
    while True:
        url = top()
        if url == None:
            time.sleep(1)
            continue
        ffmpegPath = r'ffmpeg.exe'
        a = bilibili(url, ffmpegPath)
        if not a.main():
            logError(url)
            print('记录日志完成')
        pop()


if __name__ == '__main__':
    main()
